package com.npfs.common.persistent;

import java.io.Serializable;
import java.util.Arrays;
import java.util.List;

import javax.annotation.PostConstruct;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.data.jpa.repository.support.JpaMetamodelEntityInformation;

import com.npfs.common.utils.ReflectionUtils;

@SuppressWarnings("unchecked")
public class BaseDaoImpl<T, ID extends Serializable> implements BaseDao<T, ID> {

	private static final Log log = LogFactory.getLog(BaseDaoImpl.class);

	protected Class<T> entityClass;
	protected EntityManager entityManager;
	protected String idName;

	@Override
	public int deleteByIds(Iterable<ID> ids) {
		return updateObjectUseHQL("delete from " + entityClass.getName() + " where id in ?1", ids);
	}

	@PostConstruct
	public void postConstruct() {
		entityClass = (Class<T>) ReflectionUtils.getClassGenricType(getClass(), 0);
		idName = new JpaMetamodelEntityInformation<T, Serializable>(entityClass, entityManager.getMetamodel())
				.getIdAttribute().getName();
	}

	@Override
	public List<T> queryForList(String hql, Object... params) {
		log.debug("queryForList with hql:" + hql);
		Query query = entityManager.createQuery(hql);
		setParams(query, params);
		return query.getResultList();
	}

	@Override
	public List<T> queryForListUseSQL(String sql, Object... params) {
		log.debug("queryForList with hql:" + sql);
		Query query = entityManager.createNativeQuery(sql);
		setParams(query, params);
		return query.getResultList();
	}

	@Override
	public T queryForObject(String hql, Object... params) {
		log.debug("queryForList with hql:" + hql);
		Query query = entityManager.createQuery(hql);
		setParams(query, params);
		return (T) query.getSingleResult();
	}

	@Override
	public T queryForObjectUseSQL(String sql, Object... params) {
		log.debug("queryForList with hql:" + sql);
		Query query = entityManager.createNativeQuery(sql);
		setParams(query, params);
		return (T) query.getSingleResult();
	}

	@PersistenceContext
	public void setEntityManager(EntityManager entityManager) {
		log.debug("setEntityManager for class:" + entityClass);
		this.entityManager = entityManager;
	}

	private void setParams(Query query, Object[] params) {
		log.debug("set params:" + toString(params));
		// 可变参数时要回头的下标个数
		int skip = 0;
		if (params != null && params.length != 0) {
			for (int i = 0; i < params.length; i++) {
				// 传入null时跳过此参数,下标回头
				if (params[i] == null) {
					skip++;
					continue;
				} else if (params[i] instanceof String && ((String) params[i]).startsWith("p_")
						&& ((String) params[i]).endsWith("_p")) {
					Object value = params[i + 1];
					query.setParameter((String) params[i], value);
					// named params占两个位置,所以下标+1
					i++;
				} else if (params[i] instanceof NullValue) {
					query.setParameter(i - skip + 1, null);
				} else {
					query.setParameter(i - skip + 1, params[i]);
				}
			}
		}
	}

	private String toString(Object[] params) {
		StringBuffer sb = new StringBuffer();
		sb.append("[");
		for (Object o : params) {
			if (o == null) {
				sb.append("");
			} else if (o.getClass().isArray()) {
				sb.append(Arrays.toString((Object[]) o));
			} else {
				sb.append(o);
			}
			sb.append(",");
		}
		if (sb.length() > 2) {
			sb.deleteCharAt(sb.length() - 1);
		}
		sb.append("]");
		return sb.toString();
	}

	@Override
	public int updateObjectUseHQL(String hql, Object... params) {
		Query query = entityManager.createQuery(hql);
		setParams(query, params);
		return query.executeUpdate();
	}

	@Override
	public int updateObjectUseSQL(String sql, Object... params) {
		Query query = entityManager.createNativeQuery(sql);
		setParams(query, params);
		return query.executeUpdate();
	}
}
